#include <stdio.h>
#include <stdlib.h>
#include <string.h>		// needed for strcpy()
#include <ctype.h>		// needed for isdigit()


/*
			TODO LIST:
	- Clean up script compiler
	- Support arrays with variable subscripts
	- Add music and sound functionality support
	- Support sprites and animations
	- Setup objects to have states
	- Add move/stop, float/grounded, spawn/destroy functionality to objects
*/


// This is for the new scripting format


#define NUM_INTERNAL_GVARS		91
#define NUM_INTERNAL_OVARS		24
#define NUM_INTERNAL_PVARS		90
#define NUM_PREFIXES			4
#define NUM_KEYWORDS			28
#define NUM_OPERATORS			14
#define NUM_CONDITIONS			6

char *internal_gvars[NUM_INTERNAL_GVARS] =
{
	"a0",
	"a1",
	"a2",
	"a3",
	"a4",
	"a5",
	"a6",
	"a7",
	"d0",
	"d1",
	"d2",
	"d3",
	"d4",
	"d5",
	"d6",
	"d7",
	"mouse_z_previous",
	"camera_mode",
	"fade_count",
	"edit_mode",
	"edit_selector",
	"edit_clipboard",
	"smart_mix",
	"advance_frame",
	"Game_paused",
	"DemoCount",
	"DemoPosition",
	"DemoFadeDelay",
	"zone",
	"act",
	"stage",
	"demo",
	"demo_mode",
	"frame_skip",
	"draw_frame",
	"screen_resolution_x",
	"screen_resolution_y",
	"screen_padding_x",
	"screen_padding_y",
	"screen_offset_x",
	"screen_offset_y",
	"screen_frame_x",
	"screen_frame_y",
	"screen_buffer_x",
	"screen_buffer_y",
	"screen_scale_x",
	"screen_scale_y",
	"screen_double_x",
	"screen_double_y",
	"screen_interlace_x",
	"screen_interlace_y",
	"vsync_enable",
	"close_button_pressed",
	"number_of_objects_in_memory",
	"ticCounter60L",
	"fps",
	"show_fps",
	"ticCounterL",
	"ticCounterR",
	"ticCounter60R",
	"ticUpdated",
	"waitCounter",
	"frames",
	"num_of_players",
	"num_of_frames",
	"frame",
	"Level_Inactive_flag",
	"Timer_frames",
	"Debug_object",
	"Debug_placement_mode",
	"Debug_mode_flag",
	"Emerald_count",
	"Continue_count",
	"Time_Over_flag",
	"Timer_frames",
	"Timer_minute_word",
	"Timer_minute",
	"Timer_second",
	"Timer_millisecond",
	"titlecard_sequence_end",
	"key",
	"key_shifts",
	"prompt",
	"full_screen",
	"audio_volume",
	"show_version",
	"show_watermark",
	"Update_HUD_lives",
	"Update_HUD_rings",
	"Update_HUD_timer",
	"Update_HUD_score",
};

char *internal_ovars[NUM_INTERNAL_OVARS] =
{
	"o.user",	
	"o.static_node",
	"o.htag",
	"o.ltag",
	"o.type",
	"o.routine",
	"o.status",
	"o.x_pos",
	"o.y_pos",
	"o.render_flags",
	"o.width_pixels",
	"o.priority",
	"o.subtype",
	"o.objoff_32",
	"o.objoff_34",
	"o.anim_frame_duration",
	"o.anim_frame",
	"o.anim",
	"o.x_pos_pre",
	"o.y_pos_pre",
	"o.x_vel",
	"o.y_vel",
	"o.x_radius",
	"o.y_radius",
};

char *internal_pvars[NUM_INTERNAL_PVARS] =
{
	"p.character",
	"p.sidekick",
	"p.CtrlInput",
	"p.render_flags",
	"p.art_tile",
	"p.x_pos",
	"p.x_pos_pre",
	"p.y_pos",
	"p.y_pos_pre",
	"p.x_vel",
	"p.y_vel",
	"p.inertia",
	"p.x_radius",
	"p.y_radius",
	"p.priority",
	"p.anim_frame_duration",
	"p.anim_frame",
	"p.anim",
	"p.next_anim",
	"p.status",
	"p.routine",
	"p.routine_secondary",
	"p.angle",
	"p.flip_angle",
	"p.air_left",
	"p.flip_turned",
	"p.obj_control",
	"p.status_secondary",
	"p.flips_remaining",
	"p.flip_speed",
	"p.move_lock",
	"p.invulnerable_time",
	"p.invincibility_time",
	"p.speedshoes_time",
	"p.next_tilt",
	"p.tilt",
	"p.stick_to_convex",
	"p.spindash_flag",
	"p.spindash_counter",
	"p.jumping",
	"p.interact",
	"p.layer",
	"p.layer_plus",
	"p.Sonic_top_speed",
	"p.Sonic_acceleration",
	"p.Sonic_deceleration",
	"p.Ctrl_Held",
	"p.Ctrl_Press",
	"p.Ctrl_Held_Logical",
	"p.Ctrl_Press_Logical",
	"p.width_pixels",
	"p.Sonic_Look_delay_counter",
	// "Sonic_Stat_Record_Buf[128]",
	// "Sonic_Pos_Record_Buf[128]",
	"p.Camera_X_pos",
	"p.Camera_Y_pos",
	"p.Camera_Z_pos",
	"p.Camera_Max_Y_pos",
	"p.Camera_Min_X_pos",
	"p.Camera_Max_X_pos",
	"p.Camera_Min_Y_pos",
	"p.Camera_Max_Y_pos_now",
	"p.Camera_X_pos_coarse",
	"p.Sonic_Pos_Record_Index",
	"p.Camera_Y_pos_bias",
	"p.Ring_count",
	"p.Score",
	"p.Life_count",
	"p.Extra_life_flags",
	"p.Last_star_pole_hit",
	"p.Saved_Last_star_pole_hit",
	"p.Saved_x_pos",
	"p.Saved_y_pos",
	"p.Saved_Ring_count",
	"p.Saved_Timer",
	"p.Saved_art_tile",
	"p.Saved_layer",
	"p.Saved_Camera_X_pos",
	"p.Saved_Camera_Y_pos",
	"p.Saved_Water_Level",
	"p.Saved_Extra_life_flags",
	"p.Saved_Extra_life_flags_2P",
	"p.Saved_Camera_Max_Y_pos",
	"p.Collision_addr",
	"p.ram_EEB0",
	"p.ram_EEB2",
	"p.ram_EEBE",
	"p.ram_EED0",
	"p.ram_F65C",
	"p.ram_F768",
	"p.ram_F76A",
	"p.ram_F7C7",
};

char *prefixes[NUM_PREFIXES] =
{
	"player",	// array
	"object",	// array
	"p",
	"o",
};

char *keywords[NUM_KEYWORDS] =
{
	"//",
	"/*",
	"include",
	"define",
	"object",
	"endobject",
	"if",
	"elseif",
	"else",
	"endif",
	"break",
	"function",
	"endfunction",
	"playfm",
	"playpcm",
	"resetsound",
	"textout",
	"drawsprite",
	"animate",
	"destroy",
	"return",
	"counterout",
	"fadeoutfm",
	"move",
	"spawn",
	"rest",
	"setgfxmode",
	"startzone",
};

char *operators[NUM_OPERATORS] =
{
	"~",	// 0	Bitwise NOT (one operand only)
	"++",	// 1	Increase (one operand only)
	"--",	// 2	Decrease (one operand only)
	"=",	// 3	Assign
	"+",	// 4	Add
	"-",	// 5	Subtract
	"*",	// 6	Multiply
	"/",	// 7	Divide
	"%",	// 8	Modulus
	"<<",	// 9	Bitwise shift left
	">>",	// 10	Bitwise shift right
	"&",	// 11	Bitwise AND
	"|",	// 12	Bitwise OR
	"^",	// 13	Bitwise XOR
};

char *conditions[NUM_CONDITIONS] =
{
	"==",	// Equal
	">",	// Greater
	"<",	// Less
	">=",	// Greater or equal
	"<=",	// Less or equal
	"!=",	// Not equal
};

unsigned short num_labels;
char *label;
int *label_value;



// Stuff for storing the scripts
FILE *file_script;
char *script[256];
char *script_bytecode;
char *script_bytecode_index;
int script_bytecode_size;
int script_bytecode_index_size;

// Stuff for holding keywords, labels, etc.
char text_buffer[64];
char string_cmp1[64];
char string_cmp2[64];
char string_cmp3[64];

// Stuff for keeping track of the script position
int text_pos[256];
short line_number;

// Stuff for storing general information read from the script
int byte_value;
char return_type;
char array_found;
char text_length;



unsigned int ptr_offset;	// used to offset pointers

unsigned char scope_file;
unsigned char scope_if;
unsigned char scope_object;
unsigned char scope_function;

typedef struct{
	unsigned int ptr_list[256];		// ptr_list[255] is used for 'endif'
	unsigned char ptr_type[256];	// 6 for "if", 7 for "elseif", 8 for "else"
	unsigned char num_branches;
} type_if;

type_if *if_section;



char load_script(char *filename, ...){
	int filesize;
	
	printf("Opening \"%s\"...\n", filename);
	file_script = fopen(filename, "r+b");		// UPC = User Programmable Code
	if(!file_script)
		return 0;
	
	scope_file++;
	text_pos[scope_file] = 0;
	
	fseek(file_script, 0, SEEK_END);
	filesize = ftell(file_script);
	filesize += 2;
	rewind(file_script);
	script[scope_file] = (char *)malloc(filesize);
	filesize -= 2;
	
	// These will only allocate on the first file loaded
	if(!script_bytecode){
		script_bytecode = (char *)malloc(1);
		script_bytecode[0] = 0;
	}
	if(!label){
		label = (char *)malloc(64);
		label[0] = 0;
	}
	if(!label_value){
		label_value = (int *)malloc(4);
		label_value[0] = 0;
	}
	if(!if_section){
		if_section = (type_if *)malloc(1);
	}
	
	fread(script[scope_file], 1, filesize, file_script);
	script[scope_file][filesize] = 0xA;		// new line
	script[scope_file][filesize+1] = 0;		// terminating character
	fclose(file_script);
	
	return 1;
}

char save_script(){
	short i = 0;
	char header[] = { 'P', 'R', 'O', 'C', 'O', 'D', 'E', 0 };
	
	file_script = fopen("game.pcs", "wb");
	if(!file_script){
		printf("ERROR: There was an error while trying to create the PCS file.\n");
		return 0;
	}
	
	if(script_bytecode_size > 0){
		// "PROCODE" and a byte for the script version
		fwrite(header, 1, 8, file_script);
		// Bytecode header containing pointers to various chunks of code
		fwrite(&script_bytecode_size, 1, 4, file_script);	// size of script
		fwrite(script_bytecode_index, 1, script_bytecode_index_size, file_script);
		// Word to terminate the header
		fwrite(&i, 1, 2, file_script);
		// Script
		fwrite(script_bytecode, 1, script_bytecode_size, file_script);
	}
	
	fclose(file_script);
	
	return 1;
}

char free_current_script(){
	if(script[scope_file])		free(script[scope_file]);
	scope_file--;
	printf("Script closed.\n");
	
	return 1;
}

char free_script(){
	short i;
	
	for(i=0; i < 256; i++)
		if(script[i])				free(script[i]);
	if(script_bytecode)			free(script_bytecode);
	if(script_bytecode_index)	free(script_bytecode_index);
	if(label)					free(label);
	if(label_value)				free(label_value);
	if(if_section)				free(if_section);
	
	return 1;
}



char testchar_seperator(char ch){
	// test for space, tab, and carriage
	if(ch == ' ' || ch == 0x9 || ch == 0xD)
		return ch;
	// test for new line
	if(ch == 0xA){
		line_number++;
		return ch;
	}
	if(ch == '[')
		return ch;
	if(ch == ']')
		return ch;
	// no seperator found
	return 0;
}

char testchar_newline(char ch){
	if(ch == 0xA){
		line_number++;
		return 1;
	}
	// new line not found
	return 0;
}

char testchar_legalchar(char ch){
	if(	(ch >= 'a' && ch <= 'z')
	 || (ch >= 'A' && ch <= 'Z')
	 || (ch >= '0' && ch <= '9')
	 || (ch == '.')
	 //|| ch == '[' || ch == ']'
	 || ch == '"'
	 || ch == '_'
	 || ch == '/' || ch == '*' || ch == '%'
	 || ch == '+' || ch == '-'
	 || ch == '&' || ch == '|' || ch == '^' || ch == '~'
	 || ch == '=' || ch == '>' || ch == '<' || ch == '!')
	 	return ch;
	return 0;
}

char write_char_bytecode(char ch){
	script_bytecode_size++;
	script_bytecode = (char *)realloc(script_bytecode, script_bytecode_size);
	script_bytecode[script_bytecode_size-1] = ch;
	
	return 0;
}

char write_short_bytecode(short w){
	script_bytecode_size += 2;
	script_bytecode = (char *)realloc(script_bytecode, script_bytecode_size);
	*(short *)(&script_bytecode[script_bytecode_size-2]) = w;
	
	return 0;
}

char write_long_bytecode(int lw){
	script_bytecode_size += 4;
	script_bytecode = (char *)realloc(script_bytecode, script_bytecode_size);
	*(int *)(&script_bytecode[script_bytecode_size-4]) = lw;
	
	return 0;
}

char write_bytecode_index(char ch1, char ch2, int lw){
	if(ch1 != 0){	// object lookup
		script_bytecode_index_size += 5;
		script_bytecode_index = (char *)realloc(script_bytecode_index, script_bytecode_index_size);
		script_bytecode_index[script_bytecode_index_size-5] = ch1;
	}
	else{			// function lookup
		script_bytecode_index_size += 6;
		script_bytecode_index = (char *)realloc(script_bytecode_index, script_bytecode_index_size);
		script_bytecode_index[script_bytecode_index_size-6] = ch1;
		script_bytecode_index[script_bytecode_index_size-5] = ch2;
	}
	
	// pointer
	*(int *)(&script_bytecode_index[script_bytecode_index_size-4]) = lw;
	
	return 0;
}

char upper_to_lower(char *str){
	int i;
	
	for(i=0; i < 64; i++){
		if(str[i] >= 'A' && str[i] <= 'Z')
			str[i] += 0x20;
	}
	
	return 1;
}

char read_keyword(){
	int i;
	
	byte_value = -1;
	
	// loop until a legal character is found
	while(testchar_legalchar(script[scope_file][text_pos[scope_file]]) == 0){
		text_pos[scope_file]++;
		if(script[scope_file][text_pos[scope_file]] == 0)
			return 0;	// RETURN: end of script found
	}
	
	i = 0;
	// loop until an illegal character is found
	while(testchar_legalchar(script[scope_file][text_pos[scope_file]])){
		if(i == 63)
			return -1;	// RETURN: 63 character word limit exceeded
		text_buffer[i] = script[scope_file][text_pos[scope_file]];
		text_pos[scope_file]++;
		i++;
	}
	
	array_found = (script[scope_file][text_pos[scope_file]] == '[');
	text_buffer[i] = 0;	// add terminator to string
	
	for(i=0; i < NUM_KEYWORDS; i++){
		strcpy(string_cmp1, text_buffer);
		strcpy(string_cmp2, keywords[i]);
		upper_to_lower(string_cmp1);
		upper_to_lower(string_cmp2);
		if(strcmp(string_cmp1, string_cmp2) == 0){
			byte_value = i;
			return 1;	// RETURN: keyword match found
		}
	}
	
	return -2;			// RETURN: invalid keyword
}

char read_operator(){
	int i;
	
	byte_value = -1;
	
	while(testchar_legalchar(script[scope_file][text_pos[scope_file]]) == 0){
		text_pos[scope_file]++;
		if(script[scope_file][text_pos[scope_file]] == 0xA)	// new line found
			line_number++;
		else if(script[scope_file][text_pos[scope_file]] == 0)
			return 0;	// RETURN: end of script found
	}
	
	i = 0;
	while(testchar_legalchar(script[scope_file][text_pos[scope_file]])){
		if(i == 2)
			return -1;	// RETURN: invalid operator
		text_buffer[i] = script[scope_file][text_pos[scope_file]];
		text_pos[scope_file]++;
		i++;
	}
	text_buffer[i] = 0;	// add terminator to string
	
	for(i=0; i < NUM_OPERATORS; i++){
		strcpy(string_cmp1, text_buffer);
		strcpy(string_cmp2, operators[i]);
		upper_to_lower(string_cmp1);
		upper_to_lower(string_cmp2);
		if(strcmp(string_cmp1, string_cmp2) == 0){
			byte_value = i;
			return 1;	// RETURN: operator match found
		}
	}
	
	return -2;			// RETURN: invalid operator
}

char read_logic_operator(){
	int i;
	
	byte_value = -1;
	
	while(testchar_legalchar(script[scope_file][text_pos[scope_file]]) == 0){
		text_pos[scope_file]++;
		if(script[scope_file][text_pos[scope_file]] == 0xA)	// new line found
			line_number++;
		else if(script[scope_file][text_pos[scope_file]] == 0)
			return 0;	// RETURN: end of script found
	}
	
	i = 0;
	while(testchar_legalchar(script[scope_file][text_pos[scope_file]])){
		if(i == 2)
			return -1;	// RETURN: invalid operator
		text_buffer[i] = script[scope_file][text_pos[scope_file]];
		text_pos[scope_file]++;
		i++;
	}
	text_buffer[i] = 0;	// add terminator to string
	
	for(i=0; i < NUM_CONDITIONS; i++){
		strcpy(string_cmp1, text_buffer);
		strcpy(string_cmp2, conditions[i]);
		upper_to_lower(string_cmp1);
		upper_to_lower(string_cmp2);
		if(strcmp(string_cmp1, string_cmp2) == 0){
			byte_value = i;
			return 1;	// RETURN: operator match found
		}
	}
	
	return -2;			// RETURN: invalid operator
}

char read_label(){
	int i;
	
	byte_value = -1;
	
	while(testchar_legalchar(script[scope_file][text_pos[scope_file]]) == 0){
		text_pos[scope_file]++;
		if(script[scope_file][text_pos[scope_file]] == 0xA)	// new line found
			line_number++;
		else if(script[scope_file][text_pos[scope_file]] == 0)
			return 0;	// RETURN: end of script found
	}
	
	label = (char *)realloc(label, 64 + ((int)num_labels<<6));
	
	i = 0;
	while(testchar_seperator(script[scope_file][text_pos[scope_file]]) == 0){
		label[((int)num_labels<<6) + i] = script[scope_file][text_pos[scope_file]];
		text_pos[scope_file]++;
		i++;
	}
	
	label[((int)num_labels<<6) + i] = 0;
	
	for(i=0; i < num_labels; i++){
		strcpy(string_cmp1, &label[((int)num_labels)<<6]);
		strcpy(string_cmp2, &label[i<<6]);
		upper_to_lower(string_cmp1);
		upper_to_lower(string_cmp2);
		if(strcmp(string_cmp1, string_cmp2) == 0){
			return -1;	// RETURN: label already defined
		}
	}
	
	num_labels++;
	
	return 1;			// RETURN: label successfully read
}

char read_string(){
	int i;
	
	byte_value = -1;
	
	while(testchar_legalchar(script[scope_file][text_pos[scope_file]]) == 0){
		text_pos[scope_file]++;
		if(script[scope_file][text_pos[scope_file]] == 0)
			return 0;	// RETURN: end of script found
	}
	
	if(script[scope_file][text_pos[scope_file]] != '"')
		return -2;		// RETURN: invalid string
	
	text_pos[scope_file]++;
	i = 0;
	while(script[scope_file][text_pos[scope_file]] != '"'){
		if(i == 63)
			return -1;	// RETURN: 63 character word limit exceeded
		text_buffer[i] = script[scope_file][text_pos[scope_file]];
		text_pos[scope_file]++;
		i++;
	}
	
	text_pos[scope_file]++;
	text_buffer[i] = 0;	// add terminator to string
	
	return 1;			// RETURN: string successfully read
}

char read_array(){
	int i;
	
	byte_value = -1;
	
	while(testchar_legalchar(script[scope_file][text_pos[scope_file]]) == 0){
		text_pos[scope_file]++;
		if(script[scope_file][text_pos[scope_file]] == 0xA)	// new line found
			line_number++;
		else if(script[scope_file][text_pos[scope_file]] == 0)
			return 0;	// RETURN: end of script found
	}
	
	i = 0;
	while(script[scope_file][text_pos[scope_file]] != ']'){
		text_buffer[i] = script[scope_file][text_pos[scope_file]];
		text_pos[scope_file]++;
		if(text_buffer[i] == 0x20 || text_buffer[i] == 0x9 || text_buffer[i] == 0xA || text_buffer[i] == 0xD)
			text_buffer[i] = 0;
		if(text_buffer[i] == 0)
			return 0;	// RETURN: end of script found
		else if(i == 63)
			return -3;	// RETURN: 63 character limit exceeded
		i++;
	}
	
	text_buffer[i] = 0;
	for(i=0; i < 64; i++){
		if(text_buffer[i] == 0){
			text_length = i;	// save length of text
			i = 63;
		}
	}
	
	return 1;
}

char read_number(){
	int i;
	
	byte_value = -1;
	
	while(testchar_legalchar(script[scope_file][text_pos[scope_file]]) == 0){
		text_pos[scope_file]++;
		if(script[scope_file][text_pos[scope_file]] == 0xA)	// new line found
			line_number++;
		else if(script[scope_file][text_pos[scope_file]] == 0)
			return 0;	// RETURN: end of script found
	}
	
	i = 0;
	while(testchar_seperator(script[scope_file][text_pos[scope_file]]) == 0){
		text_buffer[i] = script[scope_file][text_pos[scope_file]];
		text_pos[scope_file]++;
		i++;
	}
	
	array_found = (script[scope_file][text_pos[scope_file]] == '[');
	text_buffer[i] = 0;
	text_length = i;	// save length of text
	
	if(array_found)
		return 5;
	
	for(i=0; i < NUM_KEYWORDS; i++){
		strcpy(string_cmp1, text_buffer);
		strcpy(string_cmp2, keywords[i]);
		upper_to_lower(string_cmp1);
		upper_to_lower(string_cmp2);
		if(strcmp(string_cmp1, string_cmp2) == 0){
			return -1;	// RETURN: same name as existing keyword
		}
	}
	for(i=0; i < NUM_INTERNAL_GVARS; i++){
		strcpy(string_cmp1, text_buffer);
		strcpy(string_cmp2, internal_gvars[i]);
		upper_to_lower(string_cmp1);
		upper_to_lower(string_cmp2);
		if(strcmp(string_cmp1, string_cmp2) == 0){
			byte_value = i;
			return 1;	// RETURN: GVAR match found
		}		
	}
	for(i=0; i < NUM_INTERNAL_OVARS; i++){
		strcpy(string_cmp1, text_buffer);
		strcpy(string_cmp2, internal_ovars[i]);
		upper_to_lower(string_cmp1);
		upper_to_lower(string_cmp2);
		if(strcmp(string_cmp1, string_cmp2) == 0){
			byte_value = i;
			return 10;	// RETURN: OVAR match found
		}		
	}
	for(i=0; i < NUM_INTERNAL_PVARS; i++){
		strcpy(string_cmp1, text_buffer);
		strcpy(string_cmp2, internal_pvars[i]);
		upper_to_lower(string_cmp1);
		upper_to_lower(string_cmp2);
		if(strcmp(string_cmp1, string_cmp2) == 0){
			byte_value = i;
			return 2;	// RETURN: PVAR match found
		}		
	}
	for(i=0; i < num_labels; i++){
		strcpy(string_cmp1, text_buffer);
		strcpy(string_cmp2, &label[i<<6]);
		upper_to_lower(string_cmp1);
		upper_to_lower(string_cmp2);
		if(strcmp(string_cmp1, string_cmp2) == 0){
			byte_value = i;
			return 3;	// RETURN: LABEL match found
		}		
	}
	if(isdigit(text_buffer[0]) == 0 && text_buffer[0] != '-')
		return -2;		// RETURN: invalid number
	for(i=1; i < text_length; i++){
		if(isdigit(text_buffer[i]) == 0)
			return -2;	// RETURN: invalid number
	}
	
	byte_value = atol(text_buffer);
	
	return 4;			// RETURN: number read
}



char get_variable(){
	switch(read_number()){
		case -2:
			printf("ERROR: invalid number.\n");
			return_type = -2;
			return 0;
		case -1:
			printf("ERROR: same name as existing keyword.\n");
			return_type = -1;
			return 0;
		case 0:
			printf("End of script found.\n");
			return_type = 0;
			return 0;
		case 1:
			printf("GVAR match found for '%s'.\n", text_buffer);
			return_type = 1;
			break;
		case 10:
			printf("OVAR match found for '%s'.\n", text_buffer);
			return_type = 10;
			break;
		case 2:
			printf("PVAR match found for '%s'.\n", text_buffer);
			return_type = 2;
			break;
		case 3:
			return_type = 3;
		case 4:
			printf("ERROR: must use a variable.\n");
			return_type = 4;
			return 0;
		case 5:	// Array found
			return_type = 5;
			return 5;
	}
	
	return 1;
}

char define_label(){
	switch(read_label()){
		case -1:
			printf("ERROR: label already defined.\n");
			return_type = -1;
			return 0;
		case 0:
			printf("End of script found.\n");
			return_type = 0;
			return 0;
		case 1:
			printf("New label defined.\n");
			return_type = 1;
			break;
	}
	
	return 1;
}

char get_constant(){
	switch(read_number()){
		case -2:
			printf("ERROR: invalid number.\n");
			return_type = -2;
			return 0;
		case -1:
			printf("ERROR: same name as existing keyword.\n");
			return_type = -1;
			return 0;
		case 0:
			printf("End of script found.\n");
			return_type = 0;
			return 0;
		case 1:
			printf("ERROR: must use a constant.\n");
			return_type = 1;
			return 0;
		case 2:
			printf("ERROR: must use a constant.\n");
			return_type = 2;
			return 0;
		case 3:
			printf("LABEL match found with value of '%i'.\n", label_value[byte_value]);
			return_type = 3;
			break;
		case 4:
			printf("Number '%i' read.\n", byte_value);
			return_type = 4;
			break;
	}
	
	return 1;
}

char get_number(){
	switch(read_number()){
		case -2:
			printf("ERROR: invalid number.\n");
			return_type = -2;
			return 0;
		case -1:
			printf("ERROR: same name as existing keyword.\n");
			return_type = -1;
			return 0;
		case 0:
			printf("End of script found.\n");
			return_type = 0;
			return 0;
		case 1:
			printf("GVAR match found for '%s'.\n", text_buffer);
			return_type = 1;
			break;
		case 10:
			printf("OVAR match found for '%s'.\n", text_buffer);
			return_type = 10;
			break;
		case 2:
			printf("PVAR match found for '%s'.\n", text_buffer);
			return_type = 2;
			break;
		case 3:
			printf("LABEL match found with value of '%i'.\n", label_value[byte_value]);
			return_type = 3;
			break;
		case 4:
			printf("Number '%i' read.\n", byte_value);
			return_type = 4;
			break;
	}
	
	return 1;
}

char get_operator(){
	switch(read_operator()){
		case -2:
			return_type = -2;
		case -1:
			printf("ERROR: invalid operator.\n");
			return_type = -1;
			return 0;
		case 0:
			printf("End of script found.\n");
			return_type = 0;
			return 0;
		case 1:
			printf("Operator match found for '%s'.\n", text_buffer);
			return_type = 1;
			break;
	}
	
	return 1;
}

char get_logic_operator(){
	switch(read_logic_operator()){
		case -2:
			return_type = -2;
		case -1:
			printf("ERROR: invalid logic operator.\n");
			return_type = -1;
			return 0;
		case 0:
			printf("End of script found.\n");
			return_type = 0;
			return 0;
		case 1:
			printf("Logic operator match found for '%s'.\n", text_buffer);
			return_type = 1;
			break;
	}
	
	return 1;
}

char get_keyword(){
	switch(read_keyword()){
		case -2:
			//printf("ERROR: '%s' is an invalid keyword.\n", text_buffer);
			return_type = -2;
			return 0;
		case -1:
			printf("ERROR: 63 character word limit exceeded.\n");
			return_type = -1;
			return 0;
		case 0:
			printf("End of script found.\n");
			return_type = 0;
			return 0;
		case 1:
			printf("Keyword match found for '%s'.\n", text_buffer);
			return_type = 1;
			break;
	}
	
	return 1;
}

/*char get_array(){
	switch(read_array()){
		case -2:
			printf("ERROR: invalid number.\n");
			return_type = -2;
			return 0;
		case -1:
			printf("ERROR: same name as existing keyword.\n");
			return_type = -1;
			return 0;
		case 0:
			printf("End of script found.\n");
			return_type = 0;
			return 0;
		case 1:
			printf("GVAR match found for '%s'.\n", text_buffer);
			return_type = 1;
			break;
		case 10:
			printf("OVAR match found for '%s'.\n", text_buffer);
			return_type = 10;
			break;
		case 2:
			printf("PVAR match found for '%s'.\n", text_buffer);
			return_type = 2;
			break;
		case 3:
			printf("LABEL match found with value of '%i'.\n", label_value[byte_value]);
			return_type = 3;
			break;
		case 4:
			printf("Number '%i' read.\n", byte_value);
			return_type = 4;
			break;
	}
	
	return 1;
}*/


/*
char read_array(int c){
	// START OF ARRAY DETECTION CODE
	if(script[scope_file][text_pos[scope_file]] == '['){
		text_pos[scope_file]++;
		strcpy(string_cmp3, string_cmp1);	// backup string
		if(!get_number()){ return -1; }
		if(return_type == 3)	// label used
			byte_value = label_value[byte_value];
		if(script[scope_file][text_pos[scope_file]] == ']'){
			text_pos[scope_file]++;
			if(c + byte_value < NUM_INTERNAL_OVARS){
				byte_value += c;
				strcpy(string_cmp1, string_cmp3);
				strcpy(string_cmp2, internal_ovars[c]);
				if(strcmp(string_cmp1, string_cmp2) != 0){
					printf("ERROR: Subscript value is too big for array.\n");
					return -1;
				}
			}
			else{
				printf("ERROR: Subscript value is too big for array.\n");
				return -1;
			}
		}
		else{
			printf("ERROR: Invalid subscript in array.\n");
			return -1;
		}
	}
	// END OF ARRAY DETECTION CODE
	
	return 1;
}
*/


char interpret_number(){
	int c;
	
	for(c=0; c < NUM_INTERNAL_GVARS; c++){
		strcpy(string_cmp1, internal_gvars[c]);
		upper_to_lower(string_cmp1);
		if(strcmp(text_buffer, string_cmp1) == 0){
			write_char_bytecode(1 + array_found);
			write_short_bytecode(c + 0x800);
			return_type = 1;
			return return_type;
		}
	}
	for(c=0; c < NUM_INTERNAL_OVARS; c++){
		strcpy(string_cmp1, internal_ovars[c]);
		upper_to_lower(string_cmp1);
		if(strcmp(text_buffer, string_cmp1) == 0){
			write_char_bytecode(1 + array_found);
			write_short_bytecode(c + 0x100);
			return_type = 10;
			return return_type;
		}
	}
	for(c=0; c < NUM_INTERNAL_PVARS; c++){
		strcpy(string_cmp1, internal_pvars[c]);
		upper_to_lower(string_cmp1);
		if(strcmp(text_buffer, string_cmp1) == 0){
			write_char_bytecode(1 + array_found);
			write_short_bytecode(c);
			return_type = 2;
			return return_type;
		}
	}
	
	if(!array_found){	// check for constants if it's not an array
		for(c=0; c < num_labels; c++){
			strcpy(string_cmp1, &label[c<<6]);
			upper_to_lower(string_cmp1);
			if(strcmp(text_buffer, string_cmp1) == 0){
				write_char_bytecode(0);
				write_long_bytecode(label_value[c]);
				return_type = 3;
				return return_type;
			}
		}
		if(isdigit(text_buffer[0]) == 0 && text_buffer[0] != '-'){
			return_type = -2;
			return return_type;		// RETURN: invalid number
		}
		for(c=1; c < text_length; c++){
			if(isdigit(text_buffer[c]) == 0){
				return_type = -2;
				return return_type;	// RETURN: invalid number
			}
		}
	}
	else{
		return_type = -2;
		return return_type;			// RETURN: invalid number
	}
	
	write_char_bytecode(0);
	write_long_bytecode(atol(text_buffer));
	
	return_type = 4;
	return return_type;			// RETURN: number read
}


char handle_number(char size){
	if(!get_number()){ return -1; }
	
	if(return_type == 1){		// GVAR variable
		byte_value += 0x800;
		write_char_bytecode(1);
		write_short_bytecode(byte_value);
	}
	else if(return_type == 10){	// OVAR variable
		byte_value += 0x100;
		write_char_bytecode(1);
		write_short_bytecode(byte_value);
	}
	else if(return_type == 2){	// PVAR variable
		write_char_bytecode(1);
		write_short_bytecode(byte_value);
	}
	else{										// constant
		if(return_type == 3)	// label
			byte_value = label_value[byte_value];
		write_char_bytecode(0);
		switch(size){
			case 1:
				write_char_bytecode(byte_value);
				break;
			case 2:
				write_short_bytecode(byte_value);
				break;
			case 4:
				write_long_bytecode(byte_value);
		}
	}
	
	return 1;
}


char compile_script(){
	int c;
	
	array_found = 0;
	if(!get_keyword()){
		
		// check for end of script
		if(return_type == 0){
			free_current_script();
			return 0;
		}
		
		if(return_type == -2 && (scope_object != 0 || scope_function != 0)){
			write_char_bytecode(0);	// this is an assignment
			
			upper_to_lower(text_buffer);
			
			if(!interpret_number()){
				printf("ERROR: '%s' is an invalid keyword.\n", text_buffer);
				free_current_script();
				return 0;	// assignment statement not found
			}
			
			if(array_found){		// found '[' in the string
				array_found = 0;
				
				if(read_array()){
					upper_to_lower(text_buffer);
					
					if(!interpret_number()){
						printf("ERROR: '%s' is an invalid number.\n", text_buffer);
						free_current_script();
						return 0;
					}
				}
				else{
					printf("ERROR: '%s' is an invalid array.\n", text_buffer);
					free_current_script();
					return 0;
				}
			}
			/*else{
				if(!get_number()){ free_current_script(); return 0; }
				upper_to_lower(text_buffer);
				
				if(!interpret_number()){
					printf("ERROR: '%s' is an invalid number.\n", text_buffer);
					free_current_script();
					return 0;
				}
			}*/
			
			
			
			if(!get_operator()){ free_current_script(); return 0; }
			write_char_bytecode(byte_value);
			
			
			
			if(byte_value >= 3){	// another operand is required
				if(!get_number()){ free_current_script(); return 0; }
				upper_to_lower(text_buffer);
				
				if(!interpret_number()){
					printf("ERROR: '%s' is an invalid keyword.\n", text_buffer);
					free_current_script();
					return 0;	// assignment statement not found
				}
				
				if(array_found){		// found '[' in the string
					array_found = 0;
					
					if(read_array()){
						upper_to_lower(text_buffer);
						
						if(!interpret_number()){
							printf("ERROR: '%s' is an invalid number.\n", text_buffer);
							free_current_script();
							return 0;
						}
					}
					else{
						printf("ERROR: '%s' is an invalid array.\n", text_buffer);
						free_current_script();
						return 0;
					}
				}
				/*else{
					if(!get_number()){ free_current_script(); return 0; }
					upper_to_lower(text_buffer);
					
					if(!interpret_number()){
						printf("ERROR: '%s' is an invalid number.\n", text_buffer);
						free_current_script();
						return 0;
					}
				}*/
			}
			
			return 1;
		}
		else{
			printf("ERROR: '%s' is an invalid keyword.\n", text_buffer);
			free_current_script();
			return 0;
		}
		
		/*
		// check for assignment statement
		if(return_type == -2 && (scope_object != 0 || scope_function != 0)){	// invalid keyword
			for(c=0; c < NUM_INTERNAL_GVARS; c++){
				strcpy(string_cmp1, text_buffer);
				strcpy(string_cmp2, internal_gvars[c]);
				upper_to_lower(string_cmp1);
				upper_to_lower(string_cmp2);
				if(strcmp(string_cmp1, string_cmp2) == 0){
					write_char_bytecode(0);	// this is an assignment
					byte_value = c;
					byte_value += 0x800;
					write_short_bytecode(byte_value);	// this is an assignment
					
					if(!get_operator()){ free_current_script(); return 0; }
					write_char_bytecode(byte_value);
					
					if(byte_value >= 3){	// another operand is required
						if(!get_number()){ free_current_script(); return 0; }
						
						if(return_type == 1){		// GVAR variable
							byte_value += 0x800;
							write_char_bytecode(1);
							write_short_bytecode(byte_value);
						}
						else if(return_type == 10){	// OVAR variable
							//if(!read_array(byte_value)){ free_current_script(); return 0; }
							byte_value += 0x100;
							write_char_bytecode(1);
							write_short_bytecode(byte_value);
						}
						else if(return_type == 2){	// PVAR variable
							write_char_bytecode(1);
							write_short_bytecode(byte_value);
						}
						else{						// constant
							if(return_type == 3)	// label
								byte_value = label_value[byte_value];
							write_char_bytecode(0);
							write_long_bytecode(byte_value);
						}
					}
					return 1;
				}
			}
			for(c=0; c < NUM_INTERNAL_OVARS; c++){
				strcpy(string_cmp1, text_buffer);
				strcpy(string_cmp2, internal_ovars[c]);
				upper_to_lower(string_cmp1);
				upper_to_lower(string_cmp2);
				if(strcmp(string_cmp1, string_cmp2) == 0){
					write_char_bytecode(0);	// this is an assignment
					byte_value = c;
					//if(!read_array(byte_value)){ free_current_script(); return 0; }
					byte_value += 0x100;
					write_short_bytecode(byte_value);	// this is an assignment
					
					if(!get_operator()){ free_current_script(); return 0; }
					write_char_bytecode(byte_value);
					
					if(byte_value >= 3){	// another operand is required
						if(!get_number()){ free_current_script(); return 0; }
						
						if(return_type == 1){		// GVAR variable
							byte_value += 0x800;
							write_char_bytecode(1);
							write_short_bytecode(byte_value);
						}
						else if(return_type == 10){	// OVAR variable
							//if(!read_array(byte_value)){ free_current_script(); return 0; }
							byte_value += 0x100;
							write_char_bytecode(1);
							write_short_bytecode(byte_value);
						}
						else if(return_type == 2){	// PVAR variable
							write_char_bytecode(1);
							write_short_bytecode(byte_value);
						}
						else{						// constant
							if(return_type == 3)	// label
								byte_value = label_value[byte_value];
							write_char_bytecode(0);
							write_long_bytecode(byte_value);
						}
					}
					return 1;
				}
			}
			for(c=0; c < NUM_INTERNAL_PVARS; c++){
				strcpy(string_cmp1, text_buffer);
				strcpy(string_cmp2, internal_pvars[c]);
				upper_to_lower(string_cmp1);
				upper_to_lower(string_cmp2);
				if(strcmp(string_cmp1, string_cmp2) == 0){
					write_char_bytecode(0);	// this is an assignment
					byte_value = c;
					write_short_bytecode(byte_value);	// this is an assignment
					
					if(!get_operator()){ free_current_script(); return 0; }
					write_char_bytecode(byte_value);
					
					if(byte_value >= 3){
						if(!get_number()){ free_current_script(); return 0; }
						
						if(return_type == 1){		// GVAR variable
							byte_value += 0x800;
							write_char_bytecode(1);
							write_short_bytecode(byte_value);
						}
						else if(return_type == 10){	// OVAR variable
							//if(!read_array(byte_value)){ free_current_script(); return 0; }
							byte_value += 0x100;
							write_char_bytecode(1);
							write_short_bytecode(byte_value);
						}
						else if(return_type == 2){	// PVAR variable
							write_char_bytecode(1);
							write_short_bytecode(byte_value);
						}
						else{										// constant
							if(return_type == 3)	// label
								byte_value = label_value[byte_value];
							write_char_bytecode(0);
							write_long_bytecode(byte_value);
						}
					}
					return 1;
				}
			}
			printf("ERROR: '%s' is an invalid keyword.\n", text_buffer);
			free_current_script();
			return 0;	// assignment statement not found
		}
		else{
			printf("ERROR: '%s' is an invalid keyword.\n", text_buffer);
			free_current_script();
			return 0;
		}*/
	}
	
	switch(byte_value){
		case 0:		// "//"
			while(testchar_newline(script[scope_file][text_pos[scope_file]]) == 0)
				text_pos[scope_file]++;
			
			break;
		case 1:		// "/*"
			while(byte_value != 0){
				while(script[scope_file][text_pos[scope_file]] != '*'){
					if(script[scope_file][text_pos[scope_file]] == 0){
						free_current_script();
						return 0;	// RETURN: end of script found
					}
					text_pos[scope_file]++;
				}
				if(script[scope_file][text_pos[scope_file]+1] == '/'){
					text_pos[scope_file] += 2;
					byte_value = 0;
				}
				else
					text_pos[scope_file]++;
			}
			
			break;
		case 2:		// "include"
			if(scope_object != 0){
				printf("ERROR: cannot use '%s' inside an object definition.\n", text_buffer);
				free_current_script();
				return 0;
			}
			if(scope_function != 0){
				printf("ERROR: cannot use '%s' inside a function definition.\n", text_buffer);
				free_current_script();
				return 0;
			}
			if(scope_file == 255){
				printf("ERROR: too many \"include\" levels.\n");
				free_current_script();
				return 0;
			}
			
			if(!read_string()){ free_current_script(); return 0; }
			
			load_script(text_buffer);	printf("Script loaded.\n");
			while(compile_script());	printf("Script compiled.\n");
			
			break;
		case 3:		// "define"
			if(scope_object != 0){
				printf("ERROR: cannot use '%s' inside an object definition.\n", text_buffer);
				free_current_script();
				return 0;
			}
			if(scope_function != 0){
				printf("ERROR: cannot use '%s' inside a function definition.\n", text_buffer);
				free_current_script();
				return 0;
			}
			if(num_labels == 65535){
				printf("ERROR: too many constants defined.\n");
				free_current_script();
				return 0;
			}
			
			if(!define_label()){ free_current_script(); return 0; }
			if(!get_constant()){ free_current_script(); return 0; }
			
			label_value = (int *)realloc(label_value, (int)num_labels<<2);
			if(return_type == 3)	// label
				byte_value = label_value[byte_value];
			label_value[num_labels-1] = byte_value;
			
			break;
		case 4:		// "object"
			if(scope_object != 0){
				printf("ERROR: cannot define an object within another.\n");
				free_current_script();
				return 0;
			}
			if(scope_function != 0){
				printf("ERROR: cannot define an object within a function.\n");
				free_current_script();
				return 0;
			}
			scope_object = 1;
			ptr_offset = script_bytecode_size;
			
			if(!get_constant()){ free_current_script(); return 0; }
			
			if(return_type == 3)	// label
				byte_value = label_value[byte_value];
			
			if(byte_value < 1 || byte_value > 255){
				printf("ERROR: object value must be between 1 and 255.\n");
				free_current_script();
				return 0;
			}
			
			write_bytecode_index(byte_value, 0, script_bytecode_size);
			
			break;
		case 5:	// "endobject"
			if(scope_object != 1){
				printf("ERROR: no 'object' definition found.\n");
				free_current_script();
				return 0;
			}
			if(scope_if != 0){
				printf("ERROR: cannot use 'endobject' within an 'if' statement.\n");
				free_current_script();
				return 0;
			}
			scope_object = 0;
			
			write_char_bytecode(10);	// "return"
			
			break;
		case 6:		// "if"
			scope_if++;
			if(scope_if == 255){
				printf("ERROR: too many 'if' levels.\n");
				free_current_script();
				return 0;
			}
			
			if_section = (type_if *)realloc(if_section, scope_if * sizeof(type_if));
			
			if_section[scope_if-1].num_branches = 0;
			if_section[scope_if-1].ptr_list[0] = script_bytecode_size;
			if_section[scope_if-1].ptr_type[0] = byte_value;	// "if"
			
			write_char_bytecode(byte_value);
			
			write_long_bytecode(0);		// next branch ptr
			write_long_bytecode(0);		// endif ptr
			
			//////////// Conidition ////////////
			if(!handle_number(4)){ free_current_script(); return 0; }
			
			if(!get_logic_operator()){ free_current_script(); return 0; }
			write_char_bytecode(byte_value);
			
			if(!handle_number(4)){ free_current_script(); return 0; }
			break;
		case 7:		// "elseif"
			if_section[scope_if-1].num_branches++;
			if(scope_if == 0){
				printf("ERROR: cannot use %s outside an if section.\n", text_buffer);
				free_current_script();
				return 0;
			}
			
			write_char_bytecode(10);	// insert "break"
			
			// Save script address
			if_section[scope_if-1].ptr_list[if_section[scope_if-1].num_branches] = script_bytecode_size;
			if_section[scope_if-1].ptr_type[if_section[scope_if-1].num_branches] = byte_value;	// "elseif"
			
			write_char_bytecode(byte_value);
			
			write_long_bytecode(0);		// reserve space for pointer
			
			//////////// Conidition ////////////
			if(!handle_number(4)){ free_current_script(); return 0; }
			
			if(!get_logic_operator()){ free_current_script(); return 0; }
			write_char_bytecode(byte_value);
			
			if(!handle_number(4)){ free_current_script(); return 0; }
			break;
		case 8:		// "else"
			if_section[scope_if-1].num_branches++;
			if(scope_if == 0){
				printf("ERROR: cannot use %s outside an if section.\n", text_buffer);
				free_current_script();
				return 0;
			}
			
			write_char_bytecode(10);	// insert "break"
			
			// Save script address
			if_section[scope_if-1].ptr_list[if_section[scope_if-1].num_branches] = script_bytecode_size;
			if_section[scope_if-1].ptr_type[if_section[scope_if-1].num_branches] = byte_value;	// "else"
			break;
		case 9:		// "endif"
			if_section[scope_if-1].num_branches++;
			if(scope_if == 0){
				printf("ERROR: cannot use %s outside an if section.\n", text_buffer);
				free_current_script();
				return 0;
			}
			
			if(if_section[scope_if-1].ptr_type[if_section[scope_if-1].num_branches-1] != 0x8)	// not "else"
				write_char_bytecode(10);	// insert "break"
			
			// Save script address
			if_section[scope_if-1].ptr_list[if_section[scope_if-1].num_branches] = script_bytecode_size;
			
			*(int *)(&script_bytecode[if_section[scope_if-1].ptr_list[0]+5]) = script_bytecode_size - ptr_offset;
			
			for(c=0; c < if_section[scope_if-1].num_branches; c++){
				if(if_section[scope_if-1].ptr_type[c] != 8){	// not type "else"
					*(int *)(&script_bytecode[if_section[scope_if-1].ptr_list[c]+1]) = if_section[scope_if-1].ptr_list[c+1] - ptr_offset;
				}
			}
			
			scope_if--;
			break;
		case 10:	// "break"
			if(scope_object != 1){
				printf("ERROR: cannot use '%s' outside an object definition.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			break;
		case 11:	// "function"
			if(scope_function != 0){
				printf("ERROR: cannot define a function within another.\n");
				free_current_script();
				return 0;
			}
			if(scope_object != 0){
				printf("ERROR: cannot define a function within an object.\n");
				free_current_script();
				return 0;
			}
			scope_function = 1;
			ptr_offset = script_bytecode_size;
			
			if(!get_constant()){ free_current_script(); return 0; }
			
			if(return_type == 3)	// label
				byte_value = label_value[byte_value];
			
			if(byte_value < 1 || byte_value > 255){
				printf("ERROR: function value must be between 1 and 255.\n");
				free_current_script();
				return 0;
			}
			
			write_bytecode_index(0, byte_value, script_bytecode_size);
			
			break;
		case 12:	// "endfunction"
			if(scope_function != 1){
				printf("ERROR: no 'function' definition found.\n");
				free_current_script();
				return 0;
			}
			if(scope_if != 0){
				printf("ERROR: cannot use 'endfunction' within an 'if' statement.\n");
				free_current_script();
				return 0;
			}
			scope_function = 0;
			
			write_char_bytecode(10);	// "return"
			
			break;
		case 13:	// "playbgm"
			if(scope_object == 0 && scope_function == 0){
				printf("ERROR: cannot use '%s' outside a function or object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			
			write_char_bytecode(byte_value);
			
			// music
			if(!handle_number(2)){ free_current_script(); return 0; }
			// buffer
			if(!handle_number(1)){ free_current_script(); return 0; }
			// mix
			if(!handle_number(1)){ free_current_script(); return 0; }
			
			break;
		case 14:	// "playsfx"
			if(scope_object == 0 && scope_function == 0){
				printf("ERROR: cannot use '%s' outside a function or object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			
			// sound
			if(!handle_number(2)){ free_current_script(); return 0; }
			// buffer
			if(!handle_number(1)){ free_current_script(); return 0; }
			// mix
			if(!handle_number(1)){ free_current_script(); return 0; }
			
			break;
		case 15:	// "resetsound"
			if(scope_object == 0 && scope_function == 0){
				printf("ERROR: cannot use '%s' outside a function or object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			break;
		case 16:	// "textout"
			if(scope_object == 0 && scope_function == 0){
				printf("ERROR: cannot use '%s' outside a function or object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			
			// X position
			if(!handle_number(2)){ free_current_script(); return 0; }
			// Y position
			if(!handle_number(2)){ free_current_script(); return 0; }
			// Color
			if(!handle_number(1)){ free_current_script(); return 0; }
			// String
			if(!read_string()){ free_current_script(); return 0; }
			for(c=0; c < 64; c++){
				write_char_bytecode(text_buffer[c]);
				if(text_buffer[c] == 0)
					c = 64;
			}
			break;
		case 17:	// "drawsprite"
			if(scope_object == 0){
				printf("ERROR: cannot use '%s' outside an object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			
			// Layer
			if(!handle_number(1)){ free_current_script(); return 0; }
			// Drawing flags
			if(!handle_number(1)){ free_current_script(); return 0; }
			// Translucency
			if(!handle_number(1)){ free_current_script(); return 0; }
			// X position
			if(!handle_number(2)){ free_current_script(); return 0; }
			// Y position
			if(!handle_number(2)){ free_current_script(); return 0; }
			break;
		case 18:	// "animate"
			if(scope_object == 0){
				printf("ERROR: cannot use '%s' outside an object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			
			// Layer
			if(!handle_number(1)){ free_current_script(); return 0; }
			// Drawing flags
			if(!handle_number(1)){ free_current_script(); return 0; }
			// Translucency
			if(!handle_number(1)){ free_current_script(); return 0; }
			// X position
			if(!handle_number(2)){ free_current_script(); return 0; }
			// Y position
			if(!handle_number(2)){ free_current_script(); return 0; }
			break;
		case 19:	// "destroy"
			if(scope_object == 0){
				printf("ERROR: cannot use '%s' outside an object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			break;
		case 20:	// "return"
			if(scope_object == 0 && scope_function == 0){
				printf("ERROR: cannot use '%s' outside a function or object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			break;
		case 21:	// "counterout"
			if(scope_object == 0 && scope_function == 0){
				printf("ERROR: cannot use '%s' outside a function or object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			
			// Type
			if(!handle_number(1)){ free_current_script(); return 0; }
			// Fill
			if(!handle_number(1)){ free_current_script(); return 0; }
			// Number
			if(!handle_number(4)){ free_current_script(); return 0; }
			// X position
			if(!handle_number(2)){ free_current_script(); return 0; }
			// Y position
			if(!handle_number(2)){ free_current_script(); return 0; }
			break;
		case 22:	// "fadeoutfm"
			if(scope_object == 0 && scope_function == 0){
				printf("ERROR: cannot use '%s' outside a function or object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			
			// buffer
			if(!handle_number(1)){ free_current_script(); return 0; }
			break;
		case 23:	// "move"
			if(scope_object == 0){
				printf("ERROR: cannot use '%s' outside an object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			break;
		case 24:	// "spawn"
			if(scope_object == 0 && scope_function == 0){
				printf("ERROR: cannot use '%s' outside a function or object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			
			// type
			if(!handle_number(1)){ free_current_script(); return 0; }
			// subtype
			if(!handle_number(1)){ free_current_script(); return 0; }
			// high tag
			if(!handle_number(1)){ free_current_script(); return 0; }
			// low tag
			if(!handle_number(1)){ free_current_script(); return 0; }
			// x position
			if(!handle_number(2)){ free_current_script(); return 0; }
			// y position
			if(!handle_number(2)){ free_current_script(); return 0; }
			break;
		case 25:	// "rest"
			if(scope_object == 0 && scope_function == 0){
				printf("ERROR: cannot use '%s' outside a function or object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			
			// milliseconds
			if(!handle_number(2)){ free_current_script(); return 0; }
			break;
		case 26:	// "setgfxmode"
			if(scope_object == 0 && scope_function == 0){
				printf("ERROR: cannot use '%s' outside a function or object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			
			// res_x
			if(!handle_number(2)){ free_current_script(); return 0; }
			// res_y
			if(!handle_number(2)){ free_current_script(); return 0; }
			// pad_x
			if(!handle_number(2)){ free_current_script(); return 0; }
			// pad_y
			if(!handle_number(2)){ free_current_script(); return 0; }
			// dbl_x
			if(!handle_number(2)){ free_current_script(); return 0; }
			// dbl_y
			if(!handle_number(2)){ free_current_script(); return 0; }
			// full
			if(!handle_number(1)){ free_current_script(); return 0; }
			break;
		case 27:	// "startzone"
			if(scope_object == 0 && scope_function == 0){
				printf("ERROR: cannot use '%s' outside a function or object.\n", text_buffer);
				free_current_script();
				return 0;
			}
			write_char_bytecode(byte_value);
			
			// zone
			if(!handle_number(1)){ free_current_script(); return 0; }
			// act
			if(!handle_number(1)){ free_current_script(); return 0; }
			// stage
			if(!handle_number(1)){ free_current_script(); return 0; }
			break;
	}
	
	return 1;	// continue reading script
}



int main(){
	printf("ProSonic Script Compiler\nCompile: %s\nCopyright(c) Damian Grove, 2009\n\n", __DATE__);
	
	scope_file = 0xFF;	// 'scope_file' will get increased by 1, making it 0.
	
	load_script("game.txt");		printf("Script loaded.\n");
	while(compile_script());		printf("Script compiled.\n");
	save_script();					printf("Script saved.\n");
	free_script();					printf("Script closed.\n");
	
	system("PAUSE");
	return 0;
}
